using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace MomentumV2
{

    public enum SelectedInput
    {
        MouseKeyboard,
        GamePad,
        Keyboard,
    }

    public enum MouseButtons
    {
        LeftClick,
        RightClick,
        MiddleClick,
    }

    public enum Action
    {
        MoveForward,
        MoveBackward,
        RotateLeft,
        RotateRight,
        MoveCrosseyeUp,
        MoveCrosseyeDown,
        MoveCrosseyeLeft,
        MoveCrosseyeRight,
        Fire,
    }

    /// <summary>
    /// Helper for reading input from keyboard and gamepad. This class implements
    /// two types of input readings: Menu and Game input readings.
    /// Menu Input Readings are generic and will work on keyboards and gamepads.
    /// Game Input Readings are user-defined input-specific controls, this means
    /// that the user must at least select whether he wants to use Keyboard+Mouse
    /// or GamePad controls and, if he wants, he can configure the keys assigned
    /// to the different actions.
    /// </summary>
    public class mInputState
    {

        #region Instance Variables

        MouseState m_CurrentMouseState;
        KeyboardState m_CurrentKeyboardState;
        GamePadState m_CurrentGamePadState;
        MouseState m_LastMouseState;
        KeyboardState m_LastKeyboardState;
        GamePadState m_LastGamePadState;

        SelectedInput m_SelectedInput;

        // Game actions key mappings
        Buttons m_ForwardB;
        Keys m_ForwardK;
        Buttons m_BackwardB;
        Keys m_BackwardK;
        Buttons m_RotateLeftB;
        Keys m_RotateLeftK;
        Buttons m_RotateRightB;
        Keys m_RotateRightK;
        Buttons m_CrosseyeUpB;
        Keys m_CrosseyeUpK;
        Buttons m_CrosseyeDownB;
        Keys m_CrosseyeDownK;
        Buttons m_CrosseyeLeftB;
        Keys m_CrosseyeLeftK;
        Buttons m_CrosseyeRightB;
        Keys m_CrosseyeRightK;
        Buttons m_FireB;
        Keys m_FireK;
        MouseButtons m_FireM;

        // Default mappings
        const Buttons m_DefaultForwardB = Buttons.LeftThumbstickUp;
        const Keys m_DefaultForwardK = Keys.W;
        const Buttons m_DefaultBackwardB = Buttons.LeftThumbstickDown;
        const Keys m_DefaultBackwardK = Keys.S;
        const Buttons m_DefaultRotateLeftB = Buttons.LeftThumbstickLeft;
        const Keys m_DefaultRotateLeftK = Keys.A;
        const Buttons m_DefaultRotateRightB = Buttons.LeftThumbstickRight;
        const Keys m_DefaultRotateRightK = Keys.D;
        const Buttons m_DefaultCrosseyeUpB = Buttons.RightThumbstickUp;
        const Keys m_DefaultCrosseyeUpK = Keys.Up;
        const Buttons m_DefaultCrosseyeDownB = Buttons.RightThumbstickDown;
        const Keys m_DefaultCrosseyeDownK = Keys.Down;
        const Buttons m_DefaultCrosseyeLeftB = Buttons.RightThumbstickLeft;
        const Keys m_DefaultCrosseyeLeftK = Keys.Left;
        const Buttons m_DefaultCrosseyeRightB = Buttons.RightThumbstickRight;
        const Keys m_DefaultCrosseyeRightK = Keys.Right;
        const Buttons m_DefaultFireB = Buttons.A;
        const Keys m_DefaultFireK = Keys.RightControl;
        const MouseButtons m_DefaultFireM = MouseButtons.LeftClick;

        #endregion

        #region Initialization

        /// <summary>
        /// Constructs a new input state.
        /// </summary>
        public mInputState()
        {
            if (GamePad.GetState(PlayerIndex.One).IsConnected)
            {
                m_SelectedInput = SelectedInput.GamePad;
            }
            else
            {
                m_SelectedInput = SelectedInput.MouseKeyboard;
            }
            SetDefaults();
        }

        /// <summary>
        /// Set the action buttons and keys to the default mapping.
        /// </summary>
        public void SetDefaults()
        {
            m_ForwardB = m_DefaultForwardB;
            m_ForwardK = m_DefaultForwardK;
            m_BackwardB = m_DefaultBackwardB;
            m_BackwardK = m_DefaultBackwardK;
            m_RotateLeftB = m_DefaultRotateLeftB;
            m_RotateLeftK = m_DefaultRotateLeftK;
            m_RotateRightB = m_DefaultRotateRightB;
            m_RotateRightK = m_DefaultRotateRightK;
            m_CrosseyeUpB = m_DefaultCrosseyeUpB;
            m_CrosseyeUpK = m_DefaultCrosseyeUpK;
            m_CrosseyeDownB = m_DefaultCrosseyeDownB;
            m_CrosseyeDownK = m_DefaultCrosseyeDownK;
            m_CrosseyeLeftB = m_DefaultCrosseyeLeftB;
            m_CrosseyeLeftK = m_DefaultCrosseyeLeftK;
            m_CrosseyeRightB = m_DefaultCrosseyeRightB;
            m_CrosseyeRightK = m_DefaultCrosseyeRightK;
            m_FireB = m_DefaultFireB;
            m_FireK = m_DefaultFireK;
            m_FireM = m_DefaultFireM;
        }

        #endregion

        #region Properties

        public SelectedInput SelectedInput
        {
            get { return m_SelectedInput; }
            set { m_SelectedInput = value; }
        }

        public string SelectedInputName
        {
            get
            {
                switch (m_SelectedInput)
                {
                    case SelectedInput.GamePad:
                        return "GamePad";
                    case SelectedInput.Keyboard:
                        return "Keyboard";
                    case SelectedInput.MouseKeyboard:
                        return "Keyboard & Mouse";
                    default:
                        return "Unknown Selected Input!";
                }
            }
        }

        /// <summary>
        /// Checks for a "menu up" input action on either keyboard or gamepad.
        /// </summary>
        public bool MenuUp
        {
            get
            {
                return IsNewKeyPress(Keys.Up) ||
                       IsNewButtonPress(Buttons.DPadUp) ||
                       IsNewButtonPress(Buttons.LeftThumbstickUp);
            }
        }


        /// <summary>
        /// Checks for a "menu down" input action on either keyboard or gamepad.
        /// </summary>
        public bool MenuDown
        {
            get
            {
                return IsNewKeyPress(Keys.Down) ||
                       IsNewButtonPress(Buttons.DPadDown) ||
                       IsNewButtonPress(Buttons.LeftThumbstickDown);
            }
        }


        /// <summary>
        /// Checks for a "menu select" input action on either keyboard or gamepad.
        /// </summary>
        public bool MenuSelect
        {
            get
            {
                return IsNewKeyPress(Keys.Space) ||
                       IsNewKeyPress(Keys.Enter) ||
                       IsNewButtonPress(Buttons.A) ||
                       IsNewButtonPress(Buttons.Start);
            }
        }


        /// <summary>
        /// Checks for a "menu cancel" input action on either keyboard or gamepad.
        /// </summary>
        public bool MenuCancel
        {
            get
            {
                return IsNewKeyPress(Keys.Escape) ||
                       IsNewButtonPress(Buttons.B) ||
                       IsNewButtonPress(Buttons.Back);
            }
        }


        /// <summary>
        /// Checks for a "pause the game" input action on either keyboard or gamepad.
        /// </summary>
        public bool PauseGame
        {
            get
            {
                return IsNewKeyPress(Keys.Escape) ||
                       IsNewButtonPress(Buttons.Back) ||
                       IsNewButtonPress(Buttons.Start);
            }
        }

        #endregion

        #region Update

        /// <summary>
        /// Reads the latest state of the keyboard and gamepad.
        /// </summary>
        public void Update()
        {
            m_LastMouseState = m_CurrentMouseState;
            m_LastKeyboardState = m_CurrentKeyboardState;
            m_LastGamePadState = m_CurrentGamePadState;

            m_CurrentMouseState = Mouse.GetState();
            m_CurrentKeyboardState = Keyboard.GetState();
            m_CurrentGamePadState = GamePad.GetState(PlayerIndex.One);
        }

        #endregion

        #region Methods

        /// <summary>
        /// Helper for checking if a key was newly pressed during this update.
        /// </summary>
        public bool IsNewKeyPress(Keys key)
        {
            return (m_CurrentKeyboardState.IsKeyDown(key) &&
                m_LastKeyboardState.IsKeyUp(key));
        }

        /// <summary>
        /// Helper for checking if a button was newly pressed during this update.
        /// </summary>
        public bool IsNewButtonPress(Buttons button)
        {
            return (m_CurrentGamePadState.IsButtonDown(button) &&
                m_LastGamePadState.IsButtonUp(button));
        }

        /// <summary>
        /// Helper for checking if a mouse button was newly pressed during this update.
        /// </summary>
        public bool IsNewMouseButtonPress(MouseButtons mouseButton)
        {
            switch (mouseButton)
            {
                case MouseButtons.LeftClick:
                    return (m_CurrentMouseState.LeftButton == ButtonState.Pressed &&
                        m_LastMouseState.LeftButton == ButtonState.Pressed);
                case MouseButtons.RightClick:
                    return (m_CurrentMouseState.RightButton == ButtonState.Pressed &&
                        m_LastMouseState.RightButton == ButtonState.Pressed);
                case MouseButtons.MiddleClick:
                    return (m_CurrentMouseState.MiddleButton == ButtonState.Pressed &&
                        m_LastMouseState.MiddleButton == ButtonState.Pressed);
                default:
                    return false;
            }
        }

        /// <summary>
        /// Returns whether the forward button has been pressed during this update.
        /// </summary>
        public bool MovedForward()
        {
            return (IsNewButtonPress(m_ForwardB) ||
                IsNewKeyPress(m_ForwardK));
        }

        /// <summary>
        /// Returns whether the backward button has been pressed during this update.
        /// </summary>
        public bool MovedBackward()
        {
            return (IsNewButtonPress(m_BackwardB) ||
                IsNewKeyPress(m_BackwardK));
        }

        /// <summary>
        /// Returns whether the rotate left button has been pressed during this update.
        /// </summary>
        public bool RotatedLeft()
        {
            return (IsNewButtonPress(m_RotateLeftB) ||
                IsNewKeyPress(m_RotateLeftK));
        }

        /// <summary>
        /// Returns whether the rotate right button has been pressed during this update.
        /// </summary>
        public bool RotatedRight()
        {
            return (IsNewButtonPress(m_RotateRightB) ||
                IsNewKeyPress(m_RotateRightK));
        }

        /// <summary>
        /// Returns whether the fire button has been pressed during this update.
        /// </summary>
        public bool Fired()
        {
            switch (m_SelectedInput)
            {
                case SelectedInput.GamePad:
                    return IsNewButtonPress(m_FireB);
                case SelectedInput.Keyboard:
                    return IsNewKeyPress(m_FireK);
                case SelectedInput.MouseKeyboard:
                    return IsNewMouseButtonPress(m_FireM);
                default:
                    return false;
            }
        }

        /// <summary>
        /// Returns the current position of the crosseye in screen coordinates.
        /// </summary>
        // TODO: Do this method.
        public Vector2 CrosseyePosition(int sensitivity)
        {
            /// Based on the crosseye movement sensitivity, the buttons or keys
            /// pressed or the mouse position (depending on the selected input)
            /// this method will return the position of the crosseye.
            return Vector2.Zero;
        }

        public string ActionNameSeparated(Action action)
        {
            return SeparateCammelCase(ActionName(action));
        }

        protected string SeparateCammelCase(string text)
        {
            char[] charText = text.ToCharArray();
            int ini = -1;
            int end = -1;
            for (int i = 0; i < text.Length; i++)
            {
                if ('A' <= charText[i] && charText[i] <= 'Z')
                {
                    if (ini == -1)
                    {
                        ini = i;
                    }
                    else
                    {
                        end = i;
                        break;
                    }
                }
            }
            if (end != -1)
            {
                return text.Substring(ini, end-ini) + " " + SeparateCammelCase(text.Substring(end));
            }
            else
            {
                return text;
            }
        }

        public string ActionName(Action action)
        {
            switch (action)
            {
                case Action.MoveForward:
                    switch (m_SelectedInput)
                    {
                        case SelectedInput.GamePad: return m_ForwardB.ToString();
                        default: return m_ForwardK.ToString();
                    }
                case Action.MoveBackward:
                    switch (m_SelectedInput)
                    {
                        case SelectedInput.GamePad: return m_BackwardB.ToString();
                        default: return m_BackwardK.ToString();
                    }
                case Action.RotateLeft:
                    switch (m_SelectedInput)
                    {
                        case SelectedInput.GamePad: return m_RotateLeftB.ToString();
                        default: return m_RotateLeftK.ToString();
                    }
                case Action.RotateRight:
                    switch (m_SelectedInput)
                    {
                        case SelectedInput.GamePad: return m_RotateRightB.ToString();
                        default: return m_RotateRightK.ToString();
                    }
                case Action.MoveCrosseyeUp:
                    switch (m_SelectedInput)
                    {
                        case SelectedInput.GamePad: return m_CrosseyeUpB.ToString();
                        case SelectedInput.Keyboard: return m_CrosseyeUpK.ToString();
                        default: return "Mouse";
                    }
                case Action.MoveCrosseyeDown:
                    switch (m_SelectedInput)
                    {
                        case SelectedInput.GamePad: return m_CrosseyeDownB.ToString();
                        case SelectedInput.Keyboard: return m_CrosseyeDownK.ToString();
                        default: return "Mouse";
                    }
                case Action.MoveCrosseyeLeft:
                    switch (m_SelectedInput)
                    {
                        case SelectedInput.GamePad: return m_CrosseyeLeftB.ToString();
                        case SelectedInput.Keyboard: return m_CrosseyeLeftK.ToString();
                        default: return "Mouse";
                    }
                case Action.MoveCrosseyeRight:
                    switch (m_SelectedInput)
                    {
                        case SelectedInput.GamePad: return m_CrosseyeRightB.ToString();
                        case SelectedInput.Keyboard: return m_CrosseyeRightK.ToString();
                        default: return "Mouse";
                    }
                case Action.Fire:
                    switch (m_SelectedInput)
                    {
                        case SelectedInput.GamePad: return m_FireB.ToString();
                        case SelectedInput.Keyboard: return m_FireK.ToString();
                        default: return m_FireM.ToString();
                    }
                default:
                    return "UnknownAction!";
            }

        }

        #endregion

    }
}
